﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Net.Mail;

namespace Tool
{
    public class Tool
    {
        private string logDir;
        private IList<ResultStats> resultList;
        private IList<string> attachList;             

        public Tool(string logDir)
        {
            if (null == logDir || string.Empty == logDir)
            {
                this.logDir = (Directory.GetCurrentDirectory());
            }

            if (!logDir.EndsWith("\\"))
            {
                this.logDir = logDir + "\\";
            }
            else 
            {
                this.logDir = logDir;
            }
            attachList = new List<string>();
            Process();
        }

        private void Process()
        {
            DataCollector dc = new DataCollector(this.logDir);
            resultList = dc.Collect();
        }

        /// <summary>
        /// Create an HTML file based on the resultList
        /// </summary>
        /// <returns>Test result</returns>
        private string CreateHtml()
        {
            ResultStats statistics = new ResultStats();
            StringBuilder htmlBuilder = new StringBuilder();          
            htmlBuilder.Append(@"<!DOCTYPE html><html><head><meta charset=utf-8 /><title>Test Result</title>
                <style type='text/css'>header{text-align:center;width:80%;margin:0 0 6px 8%;font-family:微软雅黑;
                font-size:18px;font-weight:bold;}section{text-align:center;margin-left:20%;}table{
                border: 2px #CADEF9 solid;border-collapse: collapse;float:left;}.name{width:200px;text-align:right;}
                .num{width:100px;text-align:center;}th, td{border: 2px #CADEF9 solid;}.error{color:#F9C;}
                </style></head><body><header><h2>Test Result</h2></header><section><table border='1\'
                <tr><th class='name'>VarMap</th><th class='num'>Total</th><th class='num'>Pass</th>
                <th class='num'>Fail</th><th class='num'>Abort</th><th class='num'>Not Run</th></tr>");

            foreach (ResultStats result in resultList)
            {
                statistics.total += result.total;
                statistics.pass += result.pass;
                statistics.fail += result.fail;
                statistics.abort += result.abort;
                statistics.notRun += result.notRun;
                if (0 != result.fail || 0 != result.abort || 0 != result.notRun
                    || result.total != result.pass + result.fail + result.abort + result.notRun)
                {
                    attachList.Add(ConvertXmlToHtml(result.varMap));
                    htmlBuilder.Append(string.Format(@"<tr class='error'><td class='name'>{0}</td>
                                                    <td class='num'>{1}</td><td class='num'>{2}</td>
                                                    <td class='num'>{3}</td><td class='num'>{4}</td>
                                                    <td class='num'>{5}</td></tr>",
                                            result.varMap, result.total, result.pass, result.fail, result.abort, result.notRun));
                }
                else
                {
                    htmlBuilder.Append(string.Format(@"<tr><td class='name'>{0}</td><td class='num'>{1}</td>
                                                    <td class='num'>{2}</td><td class='num'>{3}</td>
                                                    <td class='num'>{4}</td><td class='num'>{5}</td></tr>",
                                              result.varMap, result.total, result.pass, result.fail, result.abort, result.notRun));
                }
            }

            htmlBuilder.Append(string.Format(@"<tr><td class='name'>&nbsp;</td><td class='num'>{0}</td>
                                            <td class='num'>{1}</td><td class='num'>{2}</td>
                                            <td class='num'>{3}</td><td class='num'>{4}</td></tr>",
                                    statistics.total, statistics.pass, statistics.fail, statistics.abort, statistics.notRun));
            htmlBuilder.Append(string.Format("<tr><td class='name'colspan='6'><h3>Pass Rate: {0:#.00%}</h3></td></tr>",
                                     (statistics.pass * 1.0) / statistics.total));
            htmlBuilder.Append("</table></section></body></html>");

            File.WriteAllText(logDir + "Statistics" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".html", htmlBuilder.ToString());
            return htmlBuilder.ToString();
        }

        private string ConvertXmlToHtml(string xmlFile)
        {                   
            string htmlFile = string.Format("{0}{1}-T.html", logDir, xmlFile.Remove(xmlFile.IndexOf('.')));
            System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", 
                    string.Format("/c XmlTransform.exe \"{0}{1}\" MCFLogTemplate.xsl \"{2}\"", logDir, xmlFile, htmlFile));
            processStartInfo.RedirectStandardOutput = true;
            processStartInfo.UseShellExecute = false;
            processStartInfo.CreateNoWindow = true;
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            process.StartInfo = processStartInfo;
            process.Start();
            if ("" != process.StandardOutput.ReadToEnd())
            {
                return logDir + xmlFile;
            }
            else
            {
                return htmlFile;
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="mailToList"></param>
        /// <param name="mailCCList"></param>
        /// <param name="mailFrom">Default is the user loged on</param>
        /// <param name="mailSubject"></param>
        public void SendEmail(IList<string> mailToList, IList<string> mailCCList, string mailSubject = "Test Result",
                    string userName = "SCXSVC", string password = "Neverland01!", string domain = "REDMOND")
        {
            Mail mail = new Mail("" == userName ? "SCXSVC" : userName,
                                string.Empty == password ? "Neverland01!" : password,
                                string.Empty == domain ? "REDMOND" : domain);

            foreach (string to in mailToList)
            {
                if (!mail.MailToList.Contains(to))
                {
                    mail.MailToList.Add(to);
                }
            }

            mail.MailBody = CreateHtml();

            foreach (string cc in mailCCList)
            {
                if (!mail.MailCCList.Contains(cc))
                {
                    mail.MailCCList.Add(cc);
                }
            }

            foreach (string attach in attachList)
            {
                if (!mail.AttachmentList.Contains(attach))
                {
                    mail.AttachmentList.Add(attach);
                }
            }

            mail.MailFrom = "";
            mail.Subject = mailSubject;
            mail.Send();
        }
    }

    // Result statistics datastructure for each varmap
    struct ResultStats
    {
        public string varMap;
        public int total, pass, fail, abort,notRun;        
    }

    class DataCollector 
    {       
        private string logDir;
        private XmlDocument xmlDoc;
        private XmlNodeList counterList;

        public DataCollector() : this(Directory.GetCurrentDirectory()){}

        public DataCollector(string logDir) 
        {
            this.logDir = logDir;
            xmlDoc = new XmlDocument();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="varMap">File name, does not include the path</param>
        /// <returns></returns>
        private ResultStats Analyse(string varMap)
        {
            ResultStats result = new ResultStats();
            result.varMap = varMap;

            xmlDoc.Load(logDir + "\\" + varMap);
            counterList = xmlDoc.SelectNodes("/Runs/Run/Counter");
            if (0 == counterList.Count) {
                throw new Exception("CountersNotFound");
            }

            string resultStr;
            foreach (XmlNode counter in counterList)
            {
                resultStr = counter.InnerText;
                if (resultStr.Contains("Vars expected"))
                {                   
                    result.total = Int32.Parse(resultStr.Substring(resultStr.IndexOf('[') + 1, resultStr.IndexOf(']') - resultStr.IndexOf('[') - 1));
                }
                else if (resultStr.Contains("Vars passed"))
                {
                    result.pass = Int32.Parse(resultStr.Substring(resultStr.IndexOf('[') + 1, resultStr.IndexOf(']') - resultStr.IndexOf('[') - 1));
                }
                else if (resultStr.Contains("Vars failed"))
                {
                    result.fail = Int32.Parse(resultStr.Substring(resultStr.IndexOf('[') + 1, resultStr.IndexOf(']') - resultStr.IndexOf('[') - 1));
                }
                else if (resultStr.Contains("Vars aborted"))
                {
                    result.abort = Int32.Parse(resultStr.Substring(resultStr.IndexOf('[') + 1, resultStr.IndexOf(']') - resultStr.IndexOf('[') - 1));
                }
                else if (resultStr.Contains("Vars Not Run"))
                {
                    result.notRun = Int32.Parse(resultStr.Substring(resultStr.IndexOf('[') + 1, resultStr.IndexOf(']') - resultStr.IndexOf('[') - 1));
                }
                else {
                    continue;
                }
            }
            
            return result;
        }

        public IList<ResultStats> Collect()
        {
            IList<ResultStats> resultStats = new List<ResultStats>();            
            IList<string> logFiles = Directory.GetFiles(logDir);
            foreach (string varMap in logFiles)
            {
                if (varMap.EndsWith(".xml", StringComparison.CurrentCultureIgnoreCase))
                {                   
                    try 
                    {
                        resultStats.Add(Analyse(varMap.Substring(varMap.LastIndexOf('\\') + 1)));
                    }
                    catch (Exception err)
                    {
                        if (err.Message.Contains("CountersNotFound"))
                            continue;
                        else
                            throw err;
                    }
                }
                else
                    continue;
            }

            return resultStats;
        }
    }

    class Mail
    {
        private string smtpClientStr = "smtphost";
        private IList<string> mailToList;
        private IList<string> mailCCList;
        private string mailFrom = string.Empty;
        private MailMessage mail;
        private SmtpClient smtpClient;
        private IList<string> attachmentList;

        public Mail(string userName, string password, string domain)
        {
            this.smtpClient = new SmtpClient(this.smtpClientStr);
            this.smtpClient.Credentials = new System.Net.NetworkCredential(userName, password, domain);
            this.mail = new MailMessage();
            this.mail.Priority = MailPriority.Normal;
            this.mail.IsBodyHtml = true;
        }

        public string SmtpClientStr
        {
            get {
                return this.smtpClientStr;
            }
            set {
                if (null != value && string.Empty != value)
                {
                    this.smtpClientStr = value;
                }
            }
        }

        public IList<string> MailToList
        {
            get 
            {
                if (null == this.mailToList)
                {
                    this.mailToList = new List<string>();
                }
                return this.mailToList;
            }
        }

        public IList<string> MailCCList
        {
            get 
            {
                if (null == this.mailCCList)
                {
                    this.mailCCList = new List<string>();
                }
                return this.mailCCList;
            }
        }

        public string MailFrom
        {
            get {
                return this.mailFrom;
            }
            set {
                if (null == value || string.Empty == value)
                {
                    this.mailFrom = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
                    this.mailFrom = this.MailFrom.Substring(mailFrom.IndexOf('\\') + 1);
                }
                else
                {
                    this.mailFrom = value;
                }
            }
        }

        public IList<string> AttachmentList
        {
            get {
                if (null == this.attachmentList)
                {
                    this.attachmentList = new List<string>();
                }
                return this.attachmentList;
            }
        }

        public string MailBody
        {
            get;
            set;
        }

        public string Subject
        {
            get;
            set;
        }

        public void Send()
        {           
            this.mail.From = new MailAddress(this.MailFrom + "@microsoft.com");
            this.mail.Body = this.MailBody;
            this.mail.Subject = this.Subject;
            
            foreach (string to in this.MailToList)
            {
                this.mail.To.Add(to + "@microsoft.com");
            }

            foreach (string cc in this.MailCCList)
            {
                this.mail.CC.Add(cc + "@microsoft.com");
            }

            foreach(string attach in this.AttachmentList)
            {
                this.mail.Attachments.Add(new Attachment(attach));
            }

            this.smtpClient.Send(this.mail);
        }
    }
}
